package cn.jiedanba.itext5.check;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.x509.CRLDistPoint;
import org.bouncycastle.asn1.x509.DistributionPoint;
import org.bouncycastle.cert.X509CertificateHolder;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.cert.CertificateFactory;
import java.security.cert.X509CRL;
import java.security.cert.X509CRLEntry;
import java.security.cert.X509Certificate;

/**
 * 使用CRL检查证书状态
 */
public class CRLCheck {

    private X509Certificate cert;

    public CRLCheck() {

    }

    public CRLCheck(X509Certificate cert) {
        this.cert = cert;
    }

    /**
     * 检查证书是否有效
     *
     * @return true：有效 false：无效
     */
    public boolean checkCertStatus() {
        try {
            String crlUrl = getCrlUrl(cert);
            if (crlUrl == null) {
                throw new IllegalArgumentException("证书无CRL吊销列表");
            }
            URL url = new URL(crlUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            int responseCode = connection.getResponseCode();
            if (responseCode != HttpURLConnection.HTTP_OK) {
                throw new RuntimeException("CRL下载出错");
            }
            InputStream inputStream = connection.getInputStream();

            ByteArrayOutputStream output = new ByteArrayOutputStream();
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                output.write(buffer, 0, bytesRead);
            }

            inputStream.close();
            connection.disconnect();
            byte[] crlData = output.toByteArray();
            InputStream in = new ByteArrayInputStream(crlData);
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            X509CRL crl = (X509CRL) cf.generateCRL(in);
            in.close();
            if (isRevoked(crl, cert) != null) {
                return false;
            }
            return true;
        } catch (Exception e) {
            throw new RuntimeException("CRL检查出错", e);
        }
    }

    /**
     * 从证书中获取Crl URL
     *
     * @param certificate
     * @return
     * @throws IOException
     */
    private String getCrlUrl(X509Certificate certificate) throws IOException {
        try {
            // 解析X.509证书
            X509CertificateHolder certHolder = new X509CertificateHolder(certificate.getEncoded());
            String url = null;
            // 获取CRL分发点
            CRLDistPoint crlDistPoint = CRLDistPoint.fromExtensions(certHolder.getExtensions());
            if (crlDistPoint != null) {
                DistributionPoint[] distPoints = crlDistPoint.getDistributionPoints();
                for (DistributionPoint distPoint : distPoints) {
                    ASN1Encodable generalNames = distPoint.getDistributionPoint().getName();
                    url = generalNames.toString().replaceAll("GeneralNames:", "").replaceAll("6:", "").trim();
                    break;
                }
            }
            return url;
        } catch (Exception e) {
            throw new RuntimeException("从证书中获取CRL URL出错", e);
        }

    }

    /**
     * 验证证书是否在CRL中。
     *
     * @param X509CRL         crl对象
     * @param X509Certificate 证书对象
     * @throws IOException
     */
    private X509CRLEntry isRevoked(X509CRL crl, X509Certificate cert) throws IOException {
        BigInteger sn = cert.getSerialNumber();
        X509CRLEntry set = crl.getRevokedCertificate(sn);
        if (set != null) {
            return set;
        } else {
            return null;
        }
    }
}
